home *** CD-ROM | disk | FTP | other *** search
- /*(( "Header" */
- /*
- * $Id: httpproxy.c,v 0.9 1995/12/06 19:53:55 mshopf Exp mshopf $
- *
- * (c) 1995 Matthias Hopf
- *
- * A small little Http Proxy.
- * It can serv as a ProxyProxy, too (i.e. it can perform only caching and will
- * get its data from another proxy).
- * That way it can be used for other protocol types than html, too.
- *
- * Run it standalone at high priority. It won't need much computing time as
- * it does no busy wait at all.
- * If you really want to re-get an already cached page, just reload it immedeately.
- * (no other request inbetween and no more than ReloadTime seconds delay).
- * If you browse offline, you'll get a note that the cache is invalid. Reload the
- * page if you want to queue the page and get the old cache.
- */
-
- /*
- * $Log: httpproxy.c,v $
- * Revision 0.9 1995/12/06 19:53:55 mshopf
- * added fixes for unix machines and AmiTCP4.0 compilation.
- * some bug fixes.
- * more html conform now.
- * lots of printf type fixes.
- *
- * Revision 0.8 1995/12/03 14:20:23 mshopf
- * Made auto requests and proxy messages more http conform.
- *
- * Revision 0.7 1995/11/19 17:57:50 mshopf
- * added revbump compatible version string.
- * fixed ftp offline proxyproxy std port bug.
- *
- * Revision 0.6 1995/11/04 11:26:13 mshopf
- * better shutdown (requeueing of current transmissions). small bug fixes.
- *
- * Revision 0.5 1995/11/02 18:26:13 mshopf
- * queueing system implemented.
- *
- * Revision 0.4 1995/10/21 21:28:37 mshopf
- * small bug fix.
- *
- * Revision 0.3 1995/10/17 19:23:09 mshopf
- * cleaned up messy logging system.
- * new option 'log'.
- *
- * Revision 0.2 1995/10/13 18:09:17 mshopf
- * everything works so far, exceptions are noted in the header.
- * :-)
- *
- * Revision 0.1 1995/10/13 10:44:27 mshopf
- * no caching at all right now, but it works fine as a proxyproxy.
- *
- */
-
-
- /*)) */
- /*(( "Includes" */
-
- #include "httpproxy_rev.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <assert.h>
- #include <time.h>
- #include <ctype.h>
-
- #include <syslog.h>
- #include <dirent.h>
- #include <sys/stat.h>
- #include <sys/syslog.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/ioctl.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
-
- #ifdef _AMIGA
- # include <bsdsocket.h>
- # define ioctl IoctlSocket
- # define ioctl_t long
- # define strcasecmp stricmp
- # ifdef sys_errlist
- # define strerror(x) (sys_errlist [x]) /* Won't necessary for AmiTCP4.0 */
- # endif
- # define CURRENTDIR ""
- typedef long len_t;
- #else
- # define ioctl_t int
- # define CloseSocket close
- # define CURRENTDIR "."
- typedef int len_t;
- #endif
-
- #ifdef DEBUG
- # define debug(x) printf x
- #else
- # define debug(x) (0)
- /*# define NDEBUG*/
- #endif
-
- #include <assert.h>
-
- /*)) */
- /*(( "Types / Variables / Constants" */
-
- /* Types and constants */
-
- #define CHECK_ADDRESS FALSE /* set this to TRUE, when you want to look up all addresses
- * (this is only important in proxyproxy mode, otherwise all
- * addresses need to be looked up anyway). If set to false,
- * it may be faster, but the proxy will not recognize a.b as a.b.c
- * and it cannot recognice alias names...
- * Note: Right now it is not wise to set this when you do not have
- * a personal name server, a name caching system or *big* hosts file. */
-
- #define DEFAULT_PROXYPORT 8080
- #define DEFAULT_HTTPPORT 80
- #define DEFAULT_DELTIME (48*60*60) /* default: delete cached files on startup */
- #define DEFAULT_EXPIRETIME (24*60*60) /* default: delete cache, when the page is requested and too old */
- #define DEFAULT_RELOADTIME 10 /* default: expire cache on reload inbetween */
-
- #define MAX_REQUESTS 8 /* Maximum number of pending requests */
- #define MIN_REQUESTS 4 /* Minimum number of free requests for interactive actions */
- #define MAX_URLBUFFER 1024 /* currently no Url request may be larger than this value! */
- #define MAX_URLSAVE 128 /* maximum size of saved url requests */
- #define MAX_FILENAME 20 /* maximum size of filename */
- #define MAX_CACHES 1024 /* maximum number of cache slots */
- #define MAX_DATABUFFER 2048
- #define SHIFT_DATABUFFER 512 /* when x bytes are in the data buffer, shift it after send() */
-
- #define REQ_REQSOCKET 0x01 /* a request socket is open */
- #define REQ_CONNSOCKET 0x02 /* a connection socket is open / to be connected */
- #define REQ_DONE 0x04 /* the connection socket is already closed */
- #define REQ_URLDONE 0x08 /* the URL is completely read (cache may be sent) */
- #define REQ_HTTP1X0 0x10 /* the URL was sent with HTTP/1.0 */
-
- #define CACHE_VALID 0x01 /* Cache is filled and ready to serve */
- #define CACHE_QUEUED 0x02 /* Cache is already queued for regetting */
- #define CACHE_DELETETMP 0x04 /* There's a temporaray cache file to be removed */
-
- #ifndef FALSE
- #define FALSE (0)
- #define TRUE (1)
- #endif
- /* Needed Memory */
- #define NEED_MEM ((MAX_REQUESTS * sizeof (request_t) + MAX_CACHES * sizeof (cache_t)) / 1024)
-
- #define difftime(x,y) (((u_long)(x)) - ((u_long)(y)))
- #define isvalidhttp(x) ((unsigned char)(x) > ' ' && (x) != '%' && (x) != '\\' && (unsigned char)(x) < 128)
-
- /*
- * Some typical Flag combinations in typical order:
- * None: Empty slot
- * REQ_REQSOCKET: Awaiting URL
- * REQ_REQSOCKET | REQ_CONNSOCKET: Still geting URL, but 1. line is already there
- * *or* URL done
- * Awaiting connection to remote host
- * *or* already transfering data
- * REQ_REQSOCKET | REQ_DONE: The connection socket is already closed, but data is
- * still to be delivered, or data is sent from the cache
- * REQ_CONNSOCKET: Request was terminated, still geting data to fill up
- * the cache (TIMEOUT needed!!!)
- */
-
- /* REQ_REQSOCKET: alone will never occour on ProxyProxy==TRUE... */
-
-
- typedef struct {
- int Flags;
- char File [MAX_FILENAME]; /* "" if free slot / with begining '_' (data) */
- char Url [MAX_URLSAVE]; /* "" if free slot or just getting data */
- } cache_t;
-
- typedef struct {
- long ReqSocket, ConnSocket;
- int Flags;
- char UrlBuffer [MAX_URLBUFFER];
- char DataBuffer [MAX_DATABUFFER];
- int UrlSent, UrlRecv;
- int DataSent, DataRecv;
- cache_t *Cache; /* NULL if not cacheable */
- FILE *Stream; /* != NULL on open data transfer to/from cache */
- } request_t;
-
-
- /* Global variables */
-
- char *VVersion= VERSTAG;
- char *Version = VSTRING;
- FILE *LogStream;
- char *PrgName;
- long ServerSocket;
- int StdHttpPort;
- int ServerPort = DEFAULT_PROXYPORT;
- int ProxyProxy = FALSE; /* TRUE, when all requests should be
- * forwarded to another proxy. */
- struct sockaddr_in ProxyProxyIn;
-
- cache_t *LastCache = NULL;
- time_t ThisRequestTime = 0;
- time_t StartUpTime;
- int RequestsFree = MAX_REQUESTS;
- int CachesFree = MAX_CACHES;
- long CacheNr = 0;
- u_long DelCacheTime = DEFAULT_DELTIME;
- u_long ExpireCacheTime = DEFAULT_EXPIRETIME;
- u_long ReloadCacheTime = DEFAULT_RELOADTIME;
- int OffLine = 0; /* 0: get cache files again, when they are too old */
- /* 1: keep cache files and queue them */
- int GetQueued = 0; /* 1: get queued data from remote hosts or proxyproxy */
- int CacheUnreadRequests = FALSE; /* 1: keep cache data on data connection close with data in url send buffer */
- request_t *Requests;
- cache_t *Caches;
-
-
- void DeleteConnect (request_t *Req, int ok);
- void SaveCacheUrl (cache_t *c);
-
- /*)) */
-
- /*(( "Init ()" */
-
- /* Init all global variables, open server port */
-
- void Init (char *ProxyProxyHost, int ProxyProxyPort, char *LogName)
- {
- struct servent *ServEnt;
- struct sockaddr_in SockIn;
- struct hostent *HostEnt;
- ioctl_t on = 1;
-
- /* Standard setups */
-
- if (! (LogStream = fopen (LogName, "a+")) )
- {
- fprintf (stderr, "cannot open logfile '%s': %s\n", LogName, strerror (errno));
- exit (20);
- }
-
- if (! (Requests = calloc (MAX_REQUESTS, sizeof (request_t))) )
- {
- fprintf (stderr, "not enough memory (need %d Kbyte)\n", NEED_MEM);
- exit (20);
- }
-
- if (! (Caches = calloc (MAX_CACHES, sizeof (cache_t))) )
- {
- fprintf (stderr, "not enough memory (need %d Kbyte)\n", NEED_MEM);
- exit (20);
- }
-
- ThisRequestTime = StartUpTime = time (NULL);
-
- /* Get standard HTTP port (80 at the time of development...but you never know) */
-
- if (! (ServEnt = getservbyname ("http", "tcp")) )
- {
- StdHttpPort = DEFAULT_HTTPPORT;
- syslog (LOG_WARNING, "%s: unknown protocol 'http', using default port %d", PrgName, DEFAULT_HTTPPORT);
- }
- else
- StdHttpPort = ServEnt->s_port;
-
- /* Create Serversocket */
-
- if ( (ServerSocket = socket (PF_INET, SOCK_STREAM, 0)) < 0)
- {
- fprintf (stderr, "socket() for serverport failed: %s\n", strerror (errno));
- exit (20);
- }
-
- memset ((char *) &SockIn, 0, sizeof (struct sockaddr_in));
- SockIn.sin_family = AF_INET;
- SockIn.sin_addr.s_addr = INADDR_ANY;
- SockIn.sin_port = htons (ServerPort);
- if (bind (ServerSocket, (struct sockaddr *) &SockIn, sizeof (struct sockaddr_in)) < 0)
- {
- fprintf (stderr, "bind() failed for port %d: %s\n", ServerPort, strerror (errno));
- exit (20);
- }
-
- #ifdef FIOASYNC
- if (ioctl (ServerSocket, FIOASYNC, (caddr_t) &on) < 0)
- {
- fprintf (stderr, "ioctl() failed for FIOASYNC: %s\n", strerror (errno));
- exit (20);
- }
- #endif
- #ifdef FIONBIO
- if (ioctl (ServerSocket, FIONBIO, (caddr_t) &on) < 0)
- {
- fprintf (stderr, "ioctl() failed for FIONBIO: %s\n", strerror (errno));
- exit (20);
- }
- #endif
-
- if (listen (ServerSocket, 4) < 0)
- {
- fprintf (stderr, "listen() failed: %s\n", strerror (errno));
- exit (20);
- }
-
- /* Get ProxyProxy address */
-
- if (ProxyProxyHost)
- {
- ProxyProxy = TRUE;
-
- if (! OffLine)
- {
- memset ((char *) &ProxyProxyIn, 0, sizeof (struct sockaddr_in));
- ProxyProxyIn.sin_family = AF_INET;
- ProxyProxyIn.sin_port = htons (ProxyProxyPort);
-
- if ( (ProxyProxyIn.sin_addr.s_addr = inet_addr (ProxyProxyHost)) +0 == -1) /* !!! */
- {
- if (! (HostEnt = gethostbyname (ProxyProxyHost)) )
- {
- fprintf (stderr, "can't get proxyproxy host '%s'\n", ProxyProxyHost);
- exit (20);
- }
- else
- ProxyProxyIn.sin_addr.s_addr = ((struct in_addr *)(HostEnt->h_addr)) -> s_addr;
- }
- fprintf (LogStream, "%sstarting with proxyproxy host '%s', port %d\n",
- Version, ProxyProxyHost, ProxyProxyPort);
- debug (("%sstarting with proxyproxy host '%s', port %d\n",
- Version, ProxyProxyHost, ProxyProxyPort));
- return;
- }
- }
- else if (! OffLine)
- {
- fprintf (LogStream, "%sstarting in normal mode\n", Version);
- debug (("%sstarting in normal mode\n", Version));
- return;
- }
-
- fprintf (LogStream, "%sstarting in offline mode\n", Version);
- debug (("%sstarting in offline mode\n", Version));
- }
-
-
- /*)) */
- /*(( "InitCacheSlot()/GetFreeCacheSlot()/ErrToReq()" */
-
- /* Initialize cache slot */
-
- void InitCacheSlot (cache_t *c, cache_t *Template, char Init, char Separator)
- {
- if (! Template)
- {
- c->Flags = 0;
- c->Url[0] = '\0';
- sprintf (c->File, "%c%08lx%c%08lx", Init, StartUpTime, Separator, CacheNr++);
- debug (("new cache allocated: '%s'\n", c->File));
- }
- else
- {
- c->Flags = Template->Flags & ~CACHE_VALID;
- strcpy (c->Url, Template->Url);
- sprintf (c->File, "%c%8.8s%c%8.8s", Init, & Template->File [1], Separator, & Template->File [10]);
- debug (("cache %s from template %s\n", c->File, Template->File));
- }
- }
-
-
- /* Scan for a free cache slot */
-
- cache_t *GetFreeCacheSlot (void)
- {
- cache_t *c;
- int i;
-
- debug (("new cache requested.\n"));
- if (! CachesFree)
- return (NULL);
-
- for (c = Caches, i=0; i < MAX_CACHES; c++, i++)
- if (c->Url[0] == '\0' && c->File[0] == '\0')
- {
- CachesFree--;
- InitCacheSlot (c, NULL, '_', '.');
- return (c);
- }
- assert (0); /*NOTREACHED*/
- }
-
-
- /* Type an error to a data buffer of a pending request and set everything up.
- * The error is printed into the Logstream, too. When Short == NULL, a information
- * message is sent, and nothing is printed into the Logstream. */
-
- void ErrToReq (request_t *Req, int Number, char *Short, char *Descr)
- {
- static char *Author = "<A HREF=\"http://wwwcip.informatik.uni-erlangen.de/user/mshopf\">Matthias Hopf</A>";
- Req->DataSent = 0;
- if (Short)
- {
- fprintf (LogStream, "#%02d: Error: %s\n", (int) Req->ReqSocket, Short);
- debug (("#%02d: Error: %s\n", (int) Req->ReqSocket, Short));
-
- sprintf (Req->DataBuffer, "HTTP/1.0 %03d %s\015\n"
- "Server: Httpproxy_V%d.%d\015\n"
- "Content-Type: text/html\015\n\015\n"
- "<HTML><H1>Proxy Error: %s</H1><P>\n"
- "%s<P>\n"
- "<HR><ADDRESS>%s by %s</ADDRESS>\n",
- Number, Short, VERSION, REVISION, Short, Descr, Version, Author);
- }
- else
- sprintf (Req->DataBuffer, "HTTP/1.0 %03d Proxy Message\015\n"
- "Server: Httpproxy_V%d.%d\015\n"
- "Content-Type: text/html\015\n\015\n"
- "<HTML><H3>Proxy Message:</H3><P>\n"
- "%s<P>\n"
- "<HR><ADDRESS>%s by %s</ADDRESS>\n",
- Number, VERSION, REVISION, Descr, Version, Author);
-
- Req->DataRecv = strlen (Req->DataBuffer);
- Req->Flags |= REQ_DONE;
- if (Req->Flags & REQ_CONNSOCKET)
- DeleteConnect (Req, FALSE);
- }
-
-
- /*)) */
- /*(( "RemCacheEntry()/CheckCacheTime()" */
-
- /* Remove a cache entry and its according files */
-
- void RemCacheEntry (cache_t *c)
- {
- cache_t tmp;
-
- debug (("deleting cache file '%s', additional url: %s\n", c->File, c->Url[0] ? "yes" : "no"));
- if (c->File[0] && c->File[0] != '.') /* It's no special message file */
- {
- if (remove (c->File))
- {
- fprintf (LogStream, "cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno));
- debug (("cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno)));
- }
- if (c->Url[0])
- {
- c->File[0] = '@';
- if (remove (c->File))
- {
- fprintf (LogStream, "cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno));
- debug (("cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno)));
- }
- }
- if (c->Flags & CACHE_DELETETMP)
- {
- InitCacheSlot (&tmp, c, '@', '@');
- debug (("removing temporary url file '%s'\n", tmp.File));
- if (remove (tmp.File))
- {
- fprintf (LogStream, "error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno));
- debug (("error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno)));
- }
- }
- }
-
- c->File[0] = c->Url[0] = '\0';
- c->Flags = 0;
- CachesFree++;
- }
-
-
- /* Check the modification time and date of a cache entry; set Time to 0 to force expire. */
- /* Returns -1, when the cache entry is expired or to be reloaded or not existing, the entry is removed.
- * Returns -2, when the cache entry is expired and queued. */
-
- int CheckCacheTime (cache_t *c, u_long Time)
- {
- time_t FileT = -1;
- struct stat s;
-
- if (stat (c->File, &s) >= 0)
- {
- FileT = s.st_mtime;
- debug (("local 0x%lx, access 0x%lx, diff1 %ld <?> %s\n", ThisRequestTime, FileT,
- difftime (ThisRequestTime, FileT), difftime (ThisRequestTime, FileT) > Time ? "expired" : "valid"));
- if ((! Time) || difftime (ThisRequestTime, FileT) > Time)
- {
- if (OffLine)
- {
- if (! (c->Flags & CACHE_QUEUED))
- {
- cache_t tmp;
- c->Flags |= CACHE_QUEUED;
- assert (c->Flags & CACHE_VALID);
- InitCacheSlot (&tmp, c, '@', '@'); /* Queue Url for cache entry and return -2 */
- SaveCacheUrl (&tmp);
- }
- return (-2);
- }
- else if (c->Flags & CACHE_QUEUED) /* Is the entry queued and not yet sent? */
- return (-2);
- else
- FileT = -1;
- }
- }
-
- if (FileT == -1)
- {
- if (c->Flags & CACHE_QUEUED)
- {
- debug (("cache entry '%s' not yet there (queued) - This should not happen...\n", c->File));
- fprintf (stderr, "%s: Warning! Data consistency failure, line %d\n", PrgName, __LINE__);
- return (-2);
- }
- else
- {
- debug (("cache entry '%s' expired / to be reloaded, removing\n", c->File));
- RemCacheEntry (c);
- return (-1);
- }
- }
- return (c->Flags & CACHE_QUEUED ? -2 : 0);
- }
-
- /*)) */
- /*(( "ReadCacheUrl()/SaveCacheUrl()" */
-
- /* Read the Url from a specific url file */
-
- void ReadCacheUrl (cache_t *c, char *Name)
- {
- FILE *f;
-
- c->Url[0] = ' '; /* RemCacheEntry shall remove the url file, too, in case it is called */
- switch (CheckCacheTime (c, DelCacheTime)) {
- case 0:
- case -3:
- break;
-
- case -1:
- return;
- default:
- RemCacheEntry (c);
- return;
- }
-
- if (! (f = fopen (Name, "r")) )
- {
- fprintf (LogStream, "cannot open url file '%s', removing cache entry: %s\n", Name, strerror (errno));
- debug (("cannot open url file '%s', removing cache entry: %s\n", Name, strerror (errno)));
- RemCacheEntry (c);
- return;
- }
- if (fgets (c->Url, MAX_URLSAVE, f))
- {
- switch (c->Url [strlen (c->Url) - 1]) {
- case '\n':
- case '\015':
- break;
-
- default: /* Cache entry is valid -> return */
- fclose (f);
- c->Flags = CACHE_VALID;
- debug (("valid cache entry: url '%s', data '%s'\n", c->Url, c->File));
- return;
- }
- }
- fclose (f);
- fprintf (LogStream, "corrupt cache entry for url file '%s', removing\n", Name);
- debug (("corrupt cache entry for url file '%s', removing\n", Name));
- RemCacheEntry (c);
- }
-
-
- /* Save the cache's Url to its url file */
-
- void SaveCacheUrl (cache_t *c)
- {
- FILE *UrlStream;
- cache_t tmp;
-
- c->File[0] = '@';
- debug (("writing url file '%s'\n", c->File));
- if ( (UrlStream = fopen (c->File, "w")) )
- {
- fprintf (UrlStream, "%s", c->Url); /* no error checking - well... */
- fclose (UrlStream);
- c->File[0] = '_';
- if (! (c->Flags & CACHE_QUEUED))
- c->Flags |= CACHE_VALID;
- /* fprintf (LogStream, "URL '%s' is now cached\n",
- * c->Url);*/
- if (c->Flags & CACHE_DELETETMP)
- {
- InitCacheSlot (&tmp, c, '@', '@');
- debug (("removing temporary url file '%s'\n", tmp.File));
- if (remove (tmp.File))
- {
- fprintf (LogStream, "error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno));
- debug (("error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno)));
- }
- c->Flags &= ~CACHE_DELETETMP;
- }
- }
- else
- {
- fprintf (LogStream, "cannot write cache url file '%s': %s\n",
- c->File, strerror (errno));
- debug (("cannot write cache url file '%s': %s\n",
- c->File, strerror (errno)));
- c->File[0] = '_';
- if (c->Flags & CACHE_QUEUED)
- c->File[0] = '\0';
- RemCacheEntry (c);
- }
- }
-
-
- /*)) */
- /*(( "BuildCache ()" */
-
- /* Scan cache directory, build up cache and delete invalid cache entries */
-
- void BuildCache (void)
- {
- DIR *Dir;
- cache_t *c = Caches, *cc, *ccc;
- struct dirent *d;
-
- if (! (Dir = opendir (CURRENTDIR)))
- {
- fprintf (stderr, "%s: cannot open cache directory: %s\n", PrgName, strerror (errno));
- exit (20);
- }
-
- errno = 0;
- while ( (d = readdir (Dir)) )
- {
- if (strlen (d->d_name) > MAX_FILENAME-1)
- {
- if (d->d_name[0] != '.') /* no special file... */
- fprintf (stderr, "unknown file '%s' in cache directory\n", d->d_name);
- }
- else
- {
- strcpy (c->File, d->d_name);
- if (c->File [0] == '_') /* found cache data - look for cache url */
- {
- debug (("found data '%s'\n", c->File));
- for (cc = Caches; cc != c; cc++)
- if (strcmp (&c->File[1], &cc->File[1]) == 0 && cc->File[0] == '@')
- {
- cc->File[0] = '_';
- c->File[0] = '@';
- ReadCacheUrl (cc, c->File); /* already read... */
- c->File[0] = '\0';
- break;
- }
- if (c == cc) /* not found... */
- {
- c++; /* keep it, may be we'll get the url file, too */
- if (! --CachesFree)
- {
- fprintf (LogStream, "Cache table full while recacheing\n");
- debug (("Cache table full while recacheing\n"));
- break; /* break from while - no room left... */
- }
- }
- }
- else if (c->File [0] == '@') /* found cache url - look for cache data */
- {
- debug (("found url '%s'\n", c->File));
- for (cc = Caches; cc != c; cc++)
- if (strcmp (&c->File[1], &cc->File[1]) == 0 && cc->File[0] == '_')
- {
- ReadCacheUrl (cc, c->File); /* already read... */
- c->File[0] = '\0';
- break;
- }
- if (c == cc) /* not found... */
- {
- c++; /* keep it, may be we'll get the data file, too */
- if (! --CachesFree) /* otherwise it is a queued entry */
- {
- fprintf (LogStream, "Cache table full while recacheing\n");
- debug (("Cache table full while recacheing\n"));
- break; /* break from while - no room left... */
- }
- }
- }
- else
- {
- if (c->File [0] != '.') /* .files, .httpproxy-log or standard directories (unix) */
- fprintf (stderr, "unknown file '%s' in cache directory\n", c->File);
- c->File[0] = '\0';
- }
- }
- }
- if (errno)
- fprintf (stderr, "directory error while building cache table, continueing: %s\n", strerror (errno));
- closedir (Dir);
-
- /* now remove all incomplete cachentries (and files) */
- for (cc = Caches; cc != c; cc++)
- if (cc->Url[0] == '\0' && cc->File [0] == '_')
- {
- fprintf (LogStream, "cache file '%s' invalid, removing.\n", cc->File);
- debug (("cache file '%s' invalid, removing.\n", cc->File));
- RemCacheEntry (cc);
- }
-
- /* mark all url only caches as queued */
- for (cc = Caches; cc != c; cc++)
- if (cc->File [0] == '@')
- {
- ReadCacheUrl (cc, cc->File); /* the file name remains '@...' */
- if (cc->File [0] == '@')
- {
- cc->Flags = CACHE_QUEUED; /* and ! CACHE_VAILD */
- for (ccc = Caches; ccc != c; ccc++)
- if (ccc->File [0] == '_')
- if (cc != ccc && strcmp (ccc->Url, cc->Url) == 0)
- {
- debug (("url '%s': queued url file %s associated with expired cache entry %s\n", cc->Url, cc->File, ccc->File));
- assert (ccc->Flags & CACHE_VALID);
- assert (cc->File [9] == '@'); /* it has to be a special queued url file... */
-
- ccc->Flags |= CACHE_QUEUED | CACHE_DELETETMP;
- cc->File[0] = '\0';
- RemCacheEntry (cc);
- break;
- }
- if (ccc == c)
- debug (("queued url '%s'\n", cc->Url));
- }
- }
- }
-
-
- /*)) */
- /*(( "ScanCache()" */
-
- /* Scan whether a specific cache is already there, filled and ready to serve */
- /* Returns 0: no, not there 1: yes, all set up -1: Failure
- * 2: error message in buffer */
- /* Sets up the request struct for correct filling, opens all streams, etc. */
-
- int ScanCache (request_t *Req, char *Url, int Known)
- {
- int i;
- cache_t *c;
- static time_t LastRequestTime;
-
- debug (("Searching for cache for Url '%s'\n", Url));
- LastRequestTime = ThisRequestTime;
- ThisRequestTime = time (NULL);
- debug (("Last: %ld, This: %ld, Diff: %ld, reload: %s\n", LastRequestTime, ThisRequestTime,
- difftime (ThisRequestTime, LastRequestTime),
- difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime ? "yes" : "no"));
-
- for (c = Caches, i=0; i < MAX_CACHES; c++, i++)
- if (c->Url[0])
- {
- if (strcmp (c->Url, Url) == 0)
- {
- if (c->Flags & CACHE_VALID)
- {
- switch (CheckCacheTime (c, ExpireCacheTime)) {
- case 0:
- if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
- {
- if (CheckCacheTime (c, 0) == -2) /* force queueing / reloading */
- {
- debug (("queued url on request\n"));
- ErrToReq (Req, 203, NULL, "Your request for reloading the document is queued.<BR>\n"
- "You will get the new document next time you are online.<BR>\n"
- "An expired cache entry exists and can be viewed by immedeately reloading this document.");
- return (2);
- }
- break; /* forcing reload of url */
- }
-
- if (! (Req->Stream = fopen (c->File, "r")) )
- {
- fprintf (LogStream, "cannot open cache file '%s', removing: %s\n", c->File, strerror (errno));
- debug (("cannot open cache file '%s', removing: %s\n", c->File, strerror (errno)));
- RemCacheEntry (c);
- }
- else
- {
- debug (("Found cache entry, file '%s'\n", c->File));
- Req->Cache = LastCache = c; /* ok, found cache entry */
- Req->Flags |= REQ_DONE;
- return (1);
- }
- break;
-
- /* case -1: break; */
- case -2:
- if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
- {
- debug (("Sending (invalid) cache file '%s' on request\n", c->File));
- if (! (Req->Stream = fopen (c->File, "r")) )
- {
- fprintf (LogStream, "cannot open cache file '%s', removing: %s\n", c->File, strerror (errno));
- debug (("cannot open cache file '%s', removing: %s\n", c->File, strerror (errno)));
- RemCacheEntry (c);
- break;
- }
- else
- {
- Req->Cache = c; /* ok, send invalid cache entry */
- Req->Flags |= REQ_DONE;
- return (1);
- }
- }
- else
- {
- debug (("Cache entry expired, queued\n"));
- ErrToReq (Req, 203, NULL, "Your request is queued.<BR>\n"
- "You will get the document next time you are online.<BR>\n"
- "An expired cache entry exists and can be viewed by immedeately reloading this document.");
- LastCache = c;
- return (2);
- }
- }
- }
- else /* c->Flags & CACHE_VALID */
- {
- if (c->Flags & CACHE_QUEUED)
- {
- if (OffLine)
- {
- if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
- {
- ErrToReq (Req, 404, "Already queued", "Your request is already queued.<BR>\n"
- "You will get the document next time you are online.<BR>\n"
- "You tried to get an expired cache entry, but this document is not cached right now.");
- return (2);
- }
- else
- {
- debug (("Cache entry already queued\n"));
- ErrToReq (Req, 203, NULL, "Your request is already queued.<BR>\n"
- "You will get the document next time you are online.<BR>\n"
- "There is no expired cache entry for this document.");
- LastCache = c;
- return (2);
- }
- }
- else
- {
- debug (("unqueueing and getting Cache entry\n"));
- c->Flags = 0;
- c->File[0] = '_';
- LastCache = c;
- goto HaveAlreadyCache; /* sorry... */
- }
- }
- else
- {
- debug (("Second request while getting URL\n"));
- ErrToReq (Req, 204, NULL, "The same request was sent from another connection.\n"
- "May be you requested a queued URL which is just being received.<BR>\n"
- "Please try again in a few moments.");
- LastCache = c;
- return (2);
- }
- }
- }
- }
-
- if (! (Known || ProxyProxy || OffLine)) /* not cached, unknown and online without proxyproxy... don't know where to get it */
- {
- ErrToReq (Req, 404, "Unknown host", "You tried to get a document from an unknown host. Please check the URL.");
- debug (("#%02d: unknown host: URL '%s'\n", (int) Req->ReqSocket, Url));
- return (2);
- }
-
-
- if (! (c = GetFreeCacheSlot ()) ) /* not in cache so far */
- {
- if (OffLine)
- {
- ErrToReq (Req, 500, "Cache table full", "I'm sorry, but the cache table is full.<BR>"
- "It is not possible to queue up your request.");
- return (2);
- }
- else
- {
- fprintf (LogStream, "Cache table full\n");
- debug (("Cache table full\n"));
- return (0); /* no more free entries - just proxy it */
- }
- }
-
- if (OffLine)
- {
- ErrToReq (Req, 203, NULL, "Your new request is queued.<BR>\n"
- "You will get the document next time you are online.");
- debug (("Request queued.\n"));
- strcpy (c->Url, Url);
- c->Flags = CACHE_QUEUED;
- LastCache = c;
- SaveCacheUrl (c);
- return (2);
- }
-
- HaveAlreadyCache:
-
- if (! (Req->Stream = fopen (c->File, "w")) )
- {
- fprintf (LogStream, "cannot open new cache file '%s': %s\n", c->File, strerror (errno));
- debug (("cannot open new cache file '%s': %s\n", c->File, strerror (errno)));
- c->File[0] = '\0';
- RemCacheEntry (c);
- return (0); /* just proxy it */
- }
-
- debug (("New cache file '%s'\n", c->File));
- strcpy (c->Url, Url); /* new cache entry */
- c->Flags = 0;
- Req->Cache = LastCache = c;
- return (0);
- }
-
-
- /*)) */
- /*(( "GetCacheData ()" */
-
- /* Fill request data space with data from cache
- * when a read error occures, no data will be received and ServWrite()
- * will automagically terminate the socket. */
-
- void GetCacheData (request_t *Req)
- {
- int Bytes;
-
- assert (Req->DataRecv < MAX_DATABUFFER);
- assert (Req->Stream);
-
- debug (("Getting more cache data - Url done: %s\n", Req->Flags & REQ_URLDONE ? "yes" : "no"));
-
- if ( (Bytes = fread (& Req->DataBuffer [Req->DataRecv], sizeof (char),
- MAX_DATABUFFER - Req->DataRecv, Req->Stream)) > 0)
- Req->DataRecv += Bytes;
- else
- if (! feof (Req->Stream))
- {
- fprintf (LogStream, "read error on cache file '%s': %s\n", Req->Cache->File, strerror (errno));
- debug (("read error on cache file '%s': %s\n", Req->Cache->File, strerror (errno)));
- }
- debug (("Read %d bytes\n", Bytes));
- }
-
-
- /*)) */
- /*(( "BuildFdSets ()" */
-
- /* Build fdsets (outstanding reads and writes) */
-
- void BuildFdSets (fd_set *ReadSet, fd_set *WriteSet)
- {
- int i;
- request_t *Req;
-
- FD_ZERO (ReadSet);
- FD_ZERO (WriteSet);
-
- debug (("Build: "));
- if (RequestsFree)
- {
- debug (("Server %d", (int) ServerSocket));
- FD_SET (ServerSocket, ReadSet);
- }
-
- for (i=0, Req=Requests; i < MAX_REQUESTS; i++, Req++)
- {
- if (Req->Flags & REQ_REQSOCKET)
- {
- if (Req->UrlRecv < MAX_URLBUFFER-1)
- {
- debug ((", Read Req %d", (int) Req->ReqSocket));
- FD_SET (Req->ReqSocket, ReadSet);
- }
- if ((Req->Flags & (REQ_URLDONE | REQ_DONE))
- && Req->DataRecv > Req->DataSent) /* only write when the request ist finished */
- {
- debug ((", Write Req %d", (int) Req->ReqSocket));
- FD_SET (Req->ReqSocket, WriteSet);
- }
- }
- if (Req->Flags & REQ_CONNSOCKET)
- {
- if (Req->UrlRecv > Req->UrlSent) /* Waiting for connect or sending request */
- {
- debug ((", Write Con %d", (int) Req->ConnSocket));
- FD_SET (Req->ConnSocket, WriteSet);
- }
- if (Req->DataRecv < MAX_DATABUFFER) /* Transfer Data */
- {
- debug ((", Read Con %d", (int) Req->ConnSocket));
- FD_SET (Req->ConnSocket, ReadSet);
- }
- }
- }
- debug (("\n"));
- }
-
-
- /*)) */
- /*(( "Scan/CheckUrl ()" */
-
- /* Check whether the URL is read completely. Sets REQ_URLDONE accordingly.
- * Attention! This routine relys on the fact, that ScanUrl was called already!
- * That means especially, that the first line was already read completely. */
-
- void CheckUrl (request_t *Req)
- {
- debug (("Checking Url\n"));
- if (Req->Flags & REQ_HTTP1X0)
- {
- if (strstr (Req->UrlBuffer, "\n\n") || strstr (Req->UrlBuffer, "\015\015") ||
- strstr (Req->UrlBuffer, "\n\015\n\015") || strstr (Req->UrlBuffer, "\015\n\015\n"))
- Req->Flags |= REQ_URLDONE;
- }
- else
- Req->Flags |= REQ_URLDONE;
- #ifdef DEBUG
- if (Req->Flags & REQ_URLDONE)
- debug (("http request complete\n"));
- #endif
- }
-
-
- /* Scan the URL and divide it into several parts. Understands http/0.9
- * and http/1.0 versions right now. Sets REQ_URLDONE when URL is complete. */
- /* Returns -1 in case of failure, 0 on correct termination or found cache,
- * 1: found cache slot, FileD is open, 2: failure, cache contains error */
- /* Port and Address are value-return arguments. */
-
- int ScanUrl (request_t *Req, struct in_addr *Address, int *Port)
- {
- struct hostent *HostEnt;
- struct in_addr In;
- char BufGET [12];
- char BufPROT [16];
- char BufDOK [MAX_URLSAVE + 256]; /* a critical array... but that's enough */
- char BufURL [128];
- char BufVERS [8];
- int CharsRead, CharsRead2, FoundHost = 0, i;
- char *DokPtr, *PortPtr;
- char *Host;
-
- if (sscanf (Req->UrlBuffer, "%11s %15[a-zA-Z0-9]%n", BufGET, BufPROT, &CharsRead) < 2)
- return (-1);
- debug (("GET '%s', PROT '%s'\n", BufGET, BufPROT));
- if (CharsRead >= Req->UrlRecv)
- return (-1);
- if (strcasecmp (BufGET, "get") != 0) /* no get command */
- return (-1);
- if (! ProxyProxy)
- if (strcasecmp (BufPROT, "http") != 0) /* we only support http urls so far */
- return (-1);
- if (strncmp (& Req->UrlBuffer [CharsRead], "://", 3) != 0)
- return (-1);
- if (sscanf (& Req->UrlBuffer [CharsRead+3], "%126s%n", BufURL, &CharsRead2) < 1)
- return (-1);
- debug (("URL '%s'\n", BufURL));
- CharsRead += 3 + CharsRead2;
-
- if (! (DokPtr = strchr (BufURL, '/')) ) /* no host... */
- return (-1);
- *DokPtr++ = 0;
-
- if (strcasecmp (BufPROT, "http") != 0)
- *Port = -1;
- else
- *Port = StdHttpPort;
- if ( (PortPtr = strchr (BufURL, ':')) )
- {
- *PortPtr = 0;
- *Port = atoi (PortPtr + 1);
- }
-
- Host = BufURL;
- #if (CHECK_ADDRESS != TRUE)
- if (! (ProxyProxy || OffLine))
- {
- #endif
- if ( (In.s_addr = inet_addr (BufURL)) != -1)
- {
- if ( (HostEnt = gethostbyaddr ((caddr_t) &In, sizeof (In), AF_INET)) )
- {
- debug (("gethostbyaddr () succeded\n"));
- Host = HostEnt->h_name;
- FoundHost = 1;
- }
- else
- Host = inet_ntoa (In);
- }
- else if ( (HostEnt = gethostbyname (BufURL)) ) /* try to get unique name */
- {
- debug (("gethostbyname () succeded\n"));
- Host = HostEnt->h_name;
- In.s_addr = ((struct in_addr *)(HostEnt->h_addr)) -> s_addr;
- FoundHost = 1;
- }
- #if (CHECK_ADDRESS != TRUE)
- }
- else
- FoundHost = 1; /* we do know our proxy host */
- #endif
-
- *Address = In;
-
- BufVERS [2] = 0; /* BufVERS is missused here... */
- if (*Port > -1)
- sprintf (BufDOK, "%s://%s:%d/", BufPROT, Host, *Port);
- else
- sprintf (BufDOK, "%s://%s/", BufPROT, Host);
- for (PortPtr = BufDOK + strlen (BufDOK); *DokPtr; DokPtr++, PortPtr++)
- {
- if (*DokPtr == '%')
- {
- if (! (BufVERS [0] = *++DokPtr) )
- break;
- if (! (BufVERS [1] = *++DokPtr) )
- break;
- if ( (i = strtol (BufVERS, NULL, 0x10)) )
- *PortPtr = i;
- else
- *PortPtr = '?';
- }
- else
- *PortPtr = *DokPtr;
- }
- *PortPtr = 0;
-
- debug (("Proto '%s', Host '%s', Port %d, Dok '%s'\n", BufPROT, Host, *Port, BufDOK));
-
- if (Req->UrlBuffer [CharsRead] == ' ' && CharsRead < Req->UrlRecv)
- if (sscanf (& Req->UrlBuffer [CharsRead], " %5s%*s\n", BufVERS) == 1)
- if (strcasecmp (BufVERS, "http/") == 0)
- {
- debug (("got HTTP/1.0 or greater request\n"));
- Req->Flags |= REQ_HTTP1X0;
- }
-
- CheckUrl (Req);
- if (strlen (BufDOK) > MAX_URLSAVE-1) /* That one cannot be cached */
- {
- fprintf (LogStream, "UrlBuffer size (%d chars) exceeded - document '%s' is not cached\n", MAX_URLSAVE-1, BufDOK);
- debug (("UrlBuffer size (%d chars) exceeded - document '%s' is not cached\n", MAX_URLSAVE-1, BufDOK));
- return (0);
- }
- return (ScanCache (Req, BufDOK, FoundHost));
- }
-
-
- /*)) */
- /*(( "DeleteRequest/Connect ()" */
-
- /* Close the connection socket only. When a request socket is still open
- * and no data is in the cache, close all.
- * Delete cache entry in case of an error. Check the answer whether it is
- * an error message or a regular answer. Errors should not be cached! */
-
- void DeleteConnect (request_t *Req, int ok)
- {
- /* cache_t tmp;*/
-
- debug (("DeleteCon %d\n", (int) Req->ConnSocket));
- if (Req->Flags & REQ_CONNSOCKET)
- {
- CloseSocket (Req->ConnSocket);
- Req->Flags &= ~REQ_CONNSOCKET;
- if (Req->Stream)
- fclose (Req->Stream);
- Req->Stream = NULL;
- if (ok)
- {
- if (Req->Cache && ! (Req->Cache->Flags & CACHE_QUEUED)) /* we've written into a cache file */
- {
- SaveCacheUrl (Req->Cache);
- /* if (Req->Cache->Flags & CACHE_DELETETMP)
- {
- InitCacheSlot (&tmp, Req->Cache, '@', '@');
- if (remove (tmp.File))
- {
- fprintf (LogStream, "error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno));
- debug (("error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno)));
- }
- }
- else
- SaveCacheUrl (Req->Cache);*/
- Req->Cache = NULL;
- }
- }
- else if (Req->Cache) /* not ok - a error occured */
- {
- fprintf (LogStream, "error while receiving URL '%s' - removing\n", Req->Cache->Url);
- debug (("error while receiving URL '%s' - removing\n", Req->Cache->Url));
- Req->Cache->Url[0] = '\0';
- RemCacheEntry (Req->Cache);
- Req->Cache = NULL;
- }
- }
- if (Req->Flags & REQ_REQSOCKET)
- {
- if (Req->DataRecv <= Req->DataSent)
- {
- CloseSocket (Req->ReqSocket); /* All done. Hugh! */
- Req->Flags = 0;
- }
- else
- Req->Flags |= REQ_DONE;
- }
- else
- Req->Flags = 0;
- if (! Req->Flags)
- RequestsFree++;
- }
-
-
- /* Close the request socket only. When a connection socket is already up
- * (and enough data is already read) continue filling up the cache. When
- * everything is done, close all. The URL should be checked, too.
- * When it is not complete, all connections should be terminated. */
-
- void DeleteRequest (request_t *Req)
- {
- debug (("DeleteReq %d\n", (int) Req->ReqSocket));
- if (Req->Flags & REQ_REQSOCKET)
- {
- CloseSocket (Req->ReqSocket);
- Req->Flags &= ~REQ_REQSOCKET;
- }
-
- if (Req->Flags & REQ_DONE) /* Maybe we're getting data from the cache */
- {
- if (Req->Stream)
- fclose (Req->Stream);
- Req->Stream = NULL;
- Req->Flags = 0; /* All done. Hugh! */
- debug (("All done - deleterequest\n"));
- DeleteConnect (Req, FALSE);
- }
- else if (Req->Flags & REQ_URLDONE)
- {
- if (! (Req->Flags & REQ_CONNSOCKET)) /* Aborting cache sending */
- DeleteConnect (Req, FALSE);
- else
- Req->DataSent = Req->DataRecv = 0; /* Continue getting data into the cache */
- }
- else
- DeleteConnect (Req, FALSE); /* No request known so far... */
- }
-
-
- /*)) */
- /*(( "ServConnect ()" */
-
- /* The first line of the URL is already there, so we can connect to the
- * remote host. */
-
- void ServConnect (request_t *Req)
- {
- struct sockaddr_in SockIn;
- struct sockaddr_in *SockPtr = &SockIn;
- ioctl_t on = 1;
- int Port;
-
- debug (("ServCon for Req %d; Flags %x\n", (int) Req->ReqSocket, Req->Flags));
-
- if (Req->Flags & REQ_REQSOCKET)
- {
- if (strchr (Req->UrlBuffer, '\n') == 0 &&
- strchr (Req->UrlBuffer, '\015') == 0) /* first line of URL is not there... */
- return;
-
- if (Req->Flags & (REQ_CONNSOCKET | REQ_DONE)) /* already set up */
- {
- CheckUrl (Req);
- return;
- }
- }
-
- memset ((char *) &SockIn, 0, sizeof (struct sockaddr_in));
-
- switch (ScanUrl (Req, &SockIn.sin_addr, &Port)) {
- case 0: /* need to get document from remote host */
- if (Req->Cache)
- {
- fprintf (LogStream, "#%02d: getting URL '%s'\n", (int) Req->ReqSocket, Req->Cache->Url);
- debug (("#%02d: getting URL '%s'\n", (int) Req->ReqSocket, Req->Cache->Url));
- }
- else
- {
- fprintf (LogStream, "#%02d: serving uncacheable request '%s'\n",
- (int) Req->ReqSocket, Req->UrlBuffer);
- debug (("#%02d: serving uncacheable request '%s'\n",
- (int) Req->ReqSocket, Req->UrlBuffer));
- }
- break;
-
- case 1: /* already cached - no need to connect to remote host */
- assert (Req->Cache != NULL);
- fprintf (LogStream, "#%02d: sending URL '%s' from cache '%s'\n",
- (int) Req->ReqSocket, Req->Cache->Url, Req->Cache->File);
- debug (("#%02d: sending URL '%s' from cache '%s'\n",
- (int) Req->ReqSocket, Req->Cache->Url, Req->Cache->File));
- GetCacheData (Req);
- return;
-
- case 2: /* send error message from buffer */
- return;
-
- default:
- {
- fprintf (LogStream, "#%02d: invalid request\n", (int) Req->ReqSocket);
- debug (("#%02d: invalid request\n", (int) Req->ReqSocket));
- DeleteRequest (Req);
- return;
- }
- }
-
- if (OffLine)
- {
- ErrToReq (Req, 500, "Unable to serv", "It is not possible to connect to a remote host in offline mode.<BR>\n"
- "Either the requested URL is to long to be queued or an internal error has occured. When the\n"
- "URL is rather short, please contact the author of httpproxy.");
- return;
- }
-
- if (ProxyProxy)
- SockPtr = &ProxyProxyIn;
- else
- {
- SockIn.sin_family = AF_INET;
- SockIn.sin_port = htons (Port);
- }
-
-
- if ( (Req->ConnSocket = socket (AF_INET, SOCK_STREAM, 0)) < 0) /* open connection */
- {
- syslog (LOG_ERR, "%s: socket () failed", PrgName);
- DeleteRequest (Req);
- return;
- }
-
- Req->Flags |= REQ_CONNSOCKET;
-
- #ifdef FIOASYNC
- if (ioctl (Req->ConnSocket, FIOASYNC, (caddr_t) &on) < 0)
- {
- syslog (LOG_ERR, "%s: ioctl() failed for FIOASYNC: %s", PrgName, strerror (errno));
- DeleteRequest (Req);
- return;
- }
- #endif
- #ifdef FIONBIO
- if (ioctl (Req->ConnSocket, FIONBIO, (caddr_t) &on) < 0)
- {
- syslog (LOG_ERR, "%s: ioctl() failed for FIONBIO: %s", PrgName, strerror (errno));
- DeleteRequest (Req);
- return;
- }
- #endif
-
- if (connect (Req->ConnSocket, (struct sockaddr *) SockPtr, sizeof (struct sockaddr_in)) < 0)
- {
- if (errno != EINPROGRESS)
- {
- if (ProxyProxy)
- {
- fprintf (LogStream, "#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno));
- debug (("#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno)));
- }
- else
- {
- fprintf (LogStream, "#%02d: remote host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno));
- debug (("#%02d: remote host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno)));
- }
- DeleteRequest (Req);
- return;
- }
- }
- debug (("ServCon -> Conn %d\n", (int) Req->ConnSocket));
- }
-
-
- /*)) */
- /*(( "ServServer ()" */
-
- /* Serv the server port. Accept new connections, set up request database */
-
- void ServServer (void)
- {
- struct hostent *Host;
- struct sockaddr_in PeerIn;
- len_t Len = sizeof (struct sockaddr_in);
- request_t *Req = Requests;
- ioctl_t on = 1;
-
- debug (("accept Req %d\n", (int) Req->ReqSocket));
- assert (RequestsFree > 0);
-
- while (Req->Flags) /* any Flags set -> occupied */
- Req++;
- assert (Req - Requests < MAX_REQUESTS);
-
- if ( (Req->ReqSocket = accept (ServerSocket, (struct sockaddr *) &PeerIn, &Len)) < 0)
- {
- syslog (LOG_ERR, "%s: accept() failed: %s", PrgName, strerror (errno));
- return;
- }
-
- if ( (Host = gethostbyaddr ((caddr_t) &PeerIn, sizeof (PeerIn), AF_INET)) )
- {
- fprintf (LogStream, "#%02d: new request from %s:%d\n",
- (int) Req->ReqSocket, Host->h_name, PeerIn.sin_port);
- debug (("#%02d: new request from %s:%d\n",
- (int) Req->ReqSocket, Host->h_name, PeerIn.sin_port));
- }
- else
- {
- fprintf (LogStream, "#%02d: new request from %s:%d\n",
- (int) Req->ReqSocket, inet_ntoa (PeerIn.sin_addr), PeerIn.sin_port);
- debug (("#%02d: new request from %s:%d\n",
- (int) Req->ReqSocket, inet_ntoa (PeerIn.sin_addr), PeerIn.sin_port));
- }
-
- Req->Flags |= REQ_REQSOCKET;
- Req->UrlSent = Req->UrlRecv = Req->DataSent = Req->DataRecv = 0;
- Req->Cache = NULL;
- Req->Stream = NULL;
- RequestsFree--;
-
- #ifdef FIOASYNC
- if (ioctl (Req->ReqSocket, FIOASYNC, (caddr_t) &on) < 0)
- {
- syslog (LOG_ERR, "%s: ioctl() failed for FIOASYNC: %s", PrgName, strerror (errno));
- DeleteRequest (Req);
- return;
- }
- #endif
- #ifdef FIONBIO
- if (ioctl (Req->ReqSocket, FIONBIO, (caddr_t) &on) < 0)
- {
- syslog (LOG_ERR, "%s: ioctl() failed for FIONBIO: %s", PrgName, strerror (errno));
- DeleteRequest (Req);
- return;
- }
- #endif
- }
-
-
- /*)) */
- /*(( "ServRead ()" */
-
- /* Server for all reads */
-
- void ServRead (fd_set *ReadSet)
- {
- int i, Bytes;
- register request_t *Req;
-
- if (FD_ISSET (ServerSocket, ReadSet))
- ServServer ();
- for (i=0, Req=Requests; i < MAX_REQUESTS; i++, Req++)
- {
- if ((Req->Flags & REQ_REQSOCKET) && FD_ISSET (Req->ReqSocket, ReadSet))
- {
- assert (Req->UrlRecv < MAX_URLBUFFER-1);
- if ( (Bytes = recv (Req->ReqSocket, & Req->UrlBuffer [Req->UrlRecv],
- (long) MAX_URLBUFFER-1 - Req->UrlRecv, 0)) < 0)
- {
- fprintf (LogStream, "#%02d: on receiving URL: %s\n", (int) Req->ReqSocket, strerror (errno));
- debug (("#%02d: on receiving URL: %s\n", (int) Req->ReqSocket, strerror (errno)));
- DeleteRequest (Req);
- }
- else
- {
- Req->UrlRecv += Bytes;
- Req->UrlBuffer [Req->UrlRecv] = '\0'; /* needed for strchr() */
-
- debug (("Read %d bytes from Req %d containing:\n'%s'\n", Bytes,
- (int) Req->ReqSocket, & Req->UrlBuffer [Req->UrlRecv - Bytes]));
- debug (("= 0x %08lx %08lx %08lx %08lx\n", * (long *) (Req->UrlBuffer + Req->UrlRecv - Bytes),
- * (long *) (Req->UrlBuffer + Req->UrlRecv - Bytes + 4), * (long *) (Req->UrlBuffer + Req->UrlRecv - Bytes + 8),
- * (long *) (Req->UrlBuffer + Req->UrlRecv - Bytes + 12)));
-
- if (Bytes == 0) /* request socket gone away */
- DeleteRequest (Req);
- ServConnect (Req); /* Check if anything can be done already */
- if ((Req->UrlRecv == MAX_URLBUFFER-1) && ! (Req->Flags & REQ_CONNSOCKET))
- { /* buffer full and no host specification */
- fprintf (LogStream, "#%02d: URL buffer overflow\n", (int) Req->ReqSocket);
- debug (("#%02d: URL buffer overflow\n", (int) Req->ReqSocket));
- DeleteRequest (Req);
- }
- }
- }
- if ((Req->Flags & REQ_CONNSOCKET) && FD_ISSET (Req->ConnSocket, ReadSet))
- {
- assert (Req->DataRecv < MAX_DATABUFFER);
- if ( (Bytes = recv (Req->ConnSocket, & Req->DataBuffer [Req->DataRecv],
- (long) MAX_DATABUFFER - Req->DataRecv, 0)) < 0)
- {
- if (ProxyProxy)
- {
- fprintf (LogStream, "#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno));
- debug (("#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno)));
- }
- else
- {
- fprintf (LogStream, "#%02d: on receiving data: %s\n", (int) Req->ReqSocket, strerror (errno));
- debug (("#%02d: on receiving data: %s\n", (int) Req->ReqSocket, strerror (errno)));
- }
- DeleteConnect (Req, CacheUnreadRequests);
- }
- else
- {
- debug (("Read %d bytes from Con %d\n", Bytes, (int) Req->ConnSocket));
- if (Bytes == 0) /* We're done */
- DeleteConnect (Req, TRUE);
- else if (Req->Stream)
- fwrite (& Req->DataBuffer [Req->DataRecv], sizeof (char), /* no error checking... */
- Bytes, Req->Stream);
- if (Req->Flags & REQ_REQSOCKET)
- Req->DataRecv += Bytes; /* else: only save cache data */
- }
- }
- }
- }
-
-
- /*)) */
- /*(( "ServWrite ()" */
-
- /* Server for all writes */
-
- void ServWrite (fd_set *WriteSet)
- {
- int i, Bytes;
- register request_t *Req;
-
- for (i=0, Req=Requests; i < MAX_REQUESTS; i++, Req++)
- {
- if ((Req->Flags & REQ_REQSOCKET) && FD_ISSET (Req->ReqSocket, WriteSet))
- {
- assert (Req->DataRecv > Req->DataSent);
- if ( (Bytes = send (Req->ReqSocket, & Req->DataBuffer [Req->DataSent],
- (long) Req->DataRecv - Req->DataSent, 0)) < 0)
- {
- fprintf (LogStream, "#%02d: on sending data: %s\n", (int) Req->ReqSocket, strerror (errno));
- debug (("#%02d: on sending data: %s\n", (int) Req->ReqSocket, strerror (errno)));
- DeleteRequest (Req);
- }
- else
- {
- debug (("Wrote %d bytes to Req %d\n", Bytes, (int) Req->ReqSocket));
- Req->DataSent += Bytes;
- assert (Req->DataRecv >= Req->DataSent);
- if (Bytes == 0) /* request socket is unable to receive data */
- {
- fprintf (LogStream, "#%02d: request socket unable to receive data\n", (int) Req->ReqSocket);
- debug (("#%02d: request socket unable to receive data\n", (int) Req->ReqSocket));
- DeleteRequest (Req);
- }
-
- if (Req->DataSent == Req->DataRecv) /* clear / shift data buffer */
- Req->DataSent = Req->DataRecv = 0;
- else if (Req->DataSent > SHIFT_DATABUFFER)
- {
- debug (("shifting databuffer size %d by %d bytes\n", Req->DataRecv - Req->DataSent, Req->DataSent));
- memmove (Req->DataBuffer, & Req->DataBuffer [Req->DataSent],
- Req->DataRecv - Req->DataSent);
- Req->DataRecv -= Req->DataSent;
- Req->DataSent = 0;
- }
- if (Req->DataRecv < MAX_DATABUFFER && (Req->Flags & REQ_DONE) && Req->Stream)
- GetCacheData (Req);
- if (Req->DataRecv == 0 && (Req->Flags & REQ_DONE)) /* We're already done */
- DeleteRequest (Req);
- }
- }
- if ((Req->Flags & REQ_CONNSOCKET) && FD_ISSET (Req->ConnSocket, WriteSet))
- {
- assert (Req->UrlRecv > Req->UrlSent);
- if ( (Bytes = send (Req->ConnSocket, & Req->UrlBuffer [Req->UrlSent],
- (long) Req->UrlRecv - Req->UrlSent, 0)) < 0)
- {
- if (ProxyProxy)
- {
- fprintf (LogStream, "#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno));
- debug (("#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno)));
- }
- else
- {
- fprintf (LogStream, "#%02d: on sending url: %s\n", (int) Req->ReqSocket, strerror (errno));
- debug (("#%02d: on sending url: %s\n", (int) Req->ReqSocket, strerror (errno)));
- }
- DeleteConnect (Req, FALSE);
- }
- else
- {
- debug (("Wrote %d bytes to Conn %d\n", Bytes, (int) Req->ConnSocket));
- Req->UrlSent += Bytes;
- if (Bytes == 0) /* connection socket is unable to receive url */
- {
- fprintf (LogStream, "#%02d: connection socket unable to receive url\n", (int) Req->ReqSocket);
- debug (("#%02d: connection socket unable to receive url\n", (int) Req->ReqSocket));
- DeleteConnect (Req, FALSE);
- }
- }
- }
- }
- }
-
-
- /*)) */
- /*(( "CacheInUse()/RequestQueued()" */
-
- /* Check wheather a cache entry is used in any other request */
-
- request_t *CacheInUse (cache_t *c)
- {
- request_t *Req = Requests;
- int i;
-
- for (i = 0; i < MAX_REQUESTS; i++, Req++)
- if (Req->Cache == c)
- return (Req);
- return (NULL);
- }
-
-
- /* Initiate contact to remote host for a queued request */
-
- void RequestQueued (cache_t *c)
- {
- request_t *Req = Requests;
- char Buffer [MAX_URLBUFFER];
- char *p, *cp;
-
- assert (MAX_URLBUFFER >= 4 * MAX_URLSAVE); /* to be on the save side */
- fprintf (LogStream, "initiating request for Url '%s'\n", c->Url);
-
- while (Req->Flags) /* any Flags set -> occupied */
- Req++;
- assert (Req - Requests < MAX_REQUESTS);
-
- Req->Flags = REQ_URLDONE;
- Req->UrlSent= Req->DataSent = Req->DataRecv = 0;
- Req->Cache = NULL;
- Req->Stream = NULL;
- RequestsFree--;
-
- for (p = c->Url, cp = Buffer; *p; )
- if (isvalidhttp (*p))
- *cp++ = *p++;
- else
- {
- sprintf (cp, "%%%02x", *p++);
- cp += 3;
- }
- *cp = '\0';
-
- sprintf (Req->UrlBuffer, "GET %s HTTP/1.0\nUser-Agent: %s\nAccept: */*\n\n", Buffer, Version);
- Req->UrlRecv = strlen (Req->UrlBuffer);
- debug (("initiating request '%s'\n", Req->UrlBuffer));
- if (c->File [0] == '@')
- c->Url [0] = '\0';
- RemCacheEntry (c);
- ServConnect (Req);
- }
-
-
- /*)) */
- /*(( "CheckGetQueued()" */
-
- /* Check number of pending requests and initiate getting of queued URLs */
-
- void CheckGetQueued (void)
- {
- static int Nr = 0;
- static int State = 1;
- int Found = 1;
- cache_t *c;
-
- if (State == 3 || ! GetQueued)
- return;
- while (Found && RequestsFree > MIN_REQUESTS)
- {
- Found = 0;
- debug (("State %d\n", State));
-
- switch (State) {
- case 1: /* get all CACHE_QUEUED & ! CACHE_VALID */
- for (c = & Caches [Nr]; Nr < MAX_CACHES; Nr++, c++)
- if ((c->Flags & CACHE_QUEUED) && ! (c->Flags & CACHE_VALID)) /* get urls without expired caches first */
- {
- Found++;
- if (! CacheInUse (c))
- {
- RequestQueued (c);
- break;
- }
- }
- if (Nr == MAX_CACHES)
- Nr = 0;
- if (Found)
- break;
-
- Nr = 0; /* no break! */
- State = 2;
-
- case 2:
- for (c = & Caches [Nr]; Nr < MAX_CACHES; Nr++, c++)
- if (c->Flags & CACHE_QUEUED)
- {
- assert (c->Flags & CACHE_VALID);
- Found++;
- if (! CacheInUse (c))
- {
- RequestQueued (c);
- break;
- }
- }
- if (! Found)
- State = 3;
- break;
-
- default:
- assert (0);
- }
- }
- }
-
-
- /*)) */
- /*(( "DeleteInvalidCaches()" */
-
- /* Shutdown: Close and delete all invalid cache entries. The cache database will be
- * in an inconsistent state after this operation... (missing queue entries) */
-
- void DeleteInvalidCaches (void)
- {
- cache_t *c;
- request_t *Req;
- int i;
-
- for (i = 0, c = Caches; i < MAX_CACHES; i++, c++)
- if (c->File[0] && (c->Flags & (CACHE_VALID | CACHE_QUEUED)) +0 == 0) /* !!! */
- {
- Req = CacheInUse (c);
- if (Req)
- {
- if (Req->Stream) /* close cache stream */
- fclose (Req->Stream);
- Req->Stream = NULL;
- if (c->Url[0])
- {
- fprintf (LogStream, "removing and queueing URL '%s'\n", c->Url);
- SaveCacheUrl (c);
- }
- c->Url[0] = '\0'; /* keep the Url file (cache is queued) */
- RemCacheEntry (c); /* delete cache entry, data file and intermediate url files */
- }
- else
- debug (("File '%s', Url '%s', Flags 0x%x without Request!\n", c->File, c->Url, c->Flags));
- }
- }
-
-
- /*)) */
- /*(( "main ()" */
-
- /* The main routine */
-
- void main (int argc, char **argv)
- {
- fd_set ReadSet;
- fd_set WriteSet;
- char *ProxyHost = NULL;
- char *LogFile = ".httpproxy-log";
- int ProxyPort;
-
- PrgName = *argv++;
-
- if (--argc == 1)
- if (**argv == '?')
- {
- fprintf (stderr, "Usage: %s [proxy PROXYHOST PROXYPORT] [port PORT] [cache DIR] [del SECONDS]\n"
- "[expire SECONDS] [reload SECONDS] [log FILE] [unread] [offline] [get]\n"
- "The cache keyword will change the local directory.\n", PrgName);
- exit (0);
- }
-
- while (argc)
- {
- if (strcasecmp (*argv, "proxy") == 0)
- {
- if (argc < 3)
- {
- fprintf (stderr, "%s: need two arguments for 'proxy'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 3;
- ProxyHost = *argv++;
- ProxyPort = atoi (*argv++);
- }
- else if (strcasecmp (*argv, "port") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'port'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- ServerPort = atoi (*argv++);
- }
- else if (strcasecmp (*argv, "cache") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'cache'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- if (chdir (*argv++))
- {
- fprintf (stderr, "%s: no directory '%s': %s\n", PrgName, argv[-1], strerror (errno));
- exit (1);
- }
- }
- else if (strcasecmp (*argv, "del") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'del'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- DelCacheTime = atoi (*argv++);
- }
- else if (strcasecmp (*argv, "expire") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'expire'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- ExpireCacheTime = atoi (*argv++);
- }
- else if (strcasecmp (*argv, "reload") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'reload'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- ReloadCacheTime = atoi (*argv++);
- }
- else if (strcasecmp (*argv, "log") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'log'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- LogFile = *argv++;
- }
- else if (strcasecmp (*argv, "unread") == 0)
- {
- argv++;
- argc--;
- CacheUnreadRequests = TRUE;
- }
- else if (strcasecmp (*argv, "offline") == 0)
- {
- argv++;
- argc--;
- if (GetQueued)
- {
- fprintf (stderr, "%s: you cannot specify both, 'get' and 'offline'", PrgName);
- exit (1);
- }
- OffLine = 1;
- }
- else if (strcasecmp (*argv, "get") == 0)
- {
- argv++;
- argc--;
- if (OffLine)
- {
- fprintf (stderr, "%s: you cannot specify both, 'get' and 'offline'", PrgName);
- exit (1);
- }
- GetQueued = 1;
- }
- else
- {
- fprintf (stderr, "%s: unknown option '%s'\n", PrgName, *argv);
- exit (1);
- }
- }
-
- Init (ProxyHost, ProxyPort, LogFile);
-
- BuildCache ();
- assert (FD_SETSIZE > MAX_REQUESTS * 3);
-
- /* Never return */
- for (;;)
- {
- CheckGetQueued ();
- BuildFdSets (&ReadSet, &WriteSet);
- debug (("Select... RequestsFree %d, CachesFree %d\n", RequestsFree, CachesFree));
- if (select (FD_SETSIZE, &ReadSet, &WriteSet, NULL, NULL) <= 0)
- {
- if (errno == EINTR)
- {
- fprintf (stderr, "%s: terminating due to signal\n", PrgName);
- fprintf (LogStream, "terminating due to signal\n");
- DeleteInvalidCaches ();
- fprintf (LogStream, "\n");
- exit (0);
- }
- syslog (LOG_ERR, "%s: select failed: %s", PrgName, strerror (errno));
- }
- else
- {
- ServRead (&ReadSet);
- ServWrite (&WriteSet);
- }
- }
- }
- /*)) */
-
-